#region Licence
// Copyright (c) 2008 Tasman Logic Ltd

// Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
// and associated documentation files (the "Software"), to deal in the Software without restriction, 
// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all copies 
// or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#endregion

using System;
using System.Reflection;
using System.Reflection.Emit;
using ActiveSharp.PropertyMapping;
using NUnit.Framework;

namespace ActiveSharp.Tests
{
  
    [TestFixture]
    public class OffsetFinderTests
    {

        [Test, Explicit]
        public void CanCreateDynamicAssembly()
        {
            Type dummy;
            AssemblyBuilder a = OffsetFinderGenerator.BuildOffsetGetter(out dummy);
            a.Save("ActiveSharp.OffsetFinder.Dynamic.dll");
        }

        [Test]
        public void CanFindOffsets()
        {
            Assert.AreEqual(_fooExpectedOffset,    OffsetUtil.GetOffset(this, ref _foo));
            Assert.AreEqual(_barExpectedOffset,    OffsetUtil.GetOffset(this, ref _bar));
            Assert.AreEqual(_otherExpectedOffset,  OffsetUtil.GetOffset(this, ref _other));
            Assert.AreEqual(_other2ExpectedOffset, OffsetUtil.GetOffset(this, ref _other2));
            Assert.AreEqual(_lastExpectedOffset,   OffsetUtil.GetOffset(this, ref _last));      
        }

        // If you're curious about why the actual offset values are as they are,
        // see AutoLayout here http://www.codeproject.com/dotnet/pointers.asp?df=100&forumid=15320&exp=0&select=781403
        // But note that the actual offset values are irrelevant for our purposes, all we
        // need is that each field (technically each settable property) 
        // maps to a unique offset within the containing object - which will be the case, as long
        // as each field is only used by one settable property
        // Note also that there is a 4 byte section at the start of the object, before the field start, hence the added 4 in all cases.

        static readonly int pointerSize = IntPtr.Size;  // will be 4 or 8.  don't hard code to 4 bytes, because may run on 64 bit os
        const int intSize        = sizeof(int);  // will always be 4 (see http://stackoverflow.com/questions/651956/sizeofint-on-x64 )

        string _foo;
        int _bar;
        bool _other;
        bool _other2;         
        object _last;   // declare this here, as the last declared field

        static readonly int _fooExpectedOffset = pointerSize;  // offset allows for the section (4 or 8 bytes) at start of object        
        static readonly int _lastExpectedOffset = _fooExpectedOffset + pointerSize /* size of string _foo*/ ;
                // object _last is declared at the END, but comes here in the (ordered) offsets because it is an object
        static readonly int _barExpectedOffset = _lastExpectedOffset + pointerSize /* size of object _last*/;
        static readonly int _otherExpectedOffset = _barExpectedOffset + intSize /* size of int _bar */;
        static readonly int _other2ExpectedOffset = _otherExpectedOffset + 1 /* size of bool _other */ ;


        [Test]
        public void CanMapOffsetsToFields()
        {
            OffsetToFieldMap m = new OffsetToFieldMap(GetType());
            /*
            foreach (var x in m._map.Keys)
                Console.WriteLine(x + "  " + m._map[x]);
             * */

            Assert.AreEqual("_foo", m[_fooExpectedOffset]);
            Assert.AreEqual("_bar", m[_barExpectedOffset]);
            Assert.AreEqual("_other", m[_otherExpectedOffset]);
            Assert.AreEqual("_other2", m[_other2ExpectedOffset]);
            Assert.AreEqual("_last", m[_lastExpectedOffset]);

        }



        [Test]
        public void CanMapOffsetsInDerivedClasses()
        {
            OffsetToFieldMap m = new OffsetToFieldMap(typeof(DerivedFoo));            
 
            Assert.AreEqual("_baseInt",   m[AbstractBaseFoo._baseIntExpectedOffset]);
            Assert.AreEqual("_baseBoolA", m[AbstractBaseFoo._baseBoolAExpectedOffset]);
            Assert.AreEqual("_baseBoolB", m[AbstractBaseFoo._baseBoolBExpectedOffset]);

            Assert.AreEqual("_derivedInt",  m[DerivedFoo._derivedIntExpectedOffset]);
            Assert.AreEqual("_derivedBoolA", m[DerivedFoo._derivedBoolAExpectedOffset]);
            Assert.AreEqual("_derivedBoolB", m[DerivedFoo._derivedBoolBExpectedOffset]);
            Assert.AreEqual("_derivedBoolC", m[DerivedFoo._derivedBoolCExpectedOffset]);
            Assert.AreEqual("_derivedObject", m[DerivedFoo._derivedObjectExpectedOffset]);
        }

        public abstract class AbstractBaseFoo  // make it abstract since we had problems with that in one release
        {
            int _baseInt;
            bool _baseBoolA;
            bool _baseBoolB;

            public static readonly int _baseIntExpectedOffset = pointerSize;
            public static readonly int _baseBoolAExpectedOffset = _baseIntExpectedOffset + intSize;
            public static readonly int _baseBoolBExpectedOffset = _baseBoolAExpectedOffset + 1;
        }

        public class DerivedFoo : AbstractBaseFoo
        {
            int _derivedInt;
            bool _derivedBoolA;
            bool _derivedBoolB;
            bool _derivedBoolC;
            object _derivedObject;

            // must initialise these in order, because some depend on the ones before them, and we don't get the result we want if
            // don't have in right order (see http://stackoverflow.com/questions/185384/order-of-static-constructors-initializers-in-c )
            public static readonly int _derivedBoolAExpectedOffset = _baseBoolBExpectedOffset + 1;     // slots into the "gap" after the base fields
            public static readonly int _derivedBoolBExpectedOffset = _derivedBoolAExpectedOffset + 1;  // ditto
            public static readonly int _derivedObjectExpectedOffset = _baseBoolAExpectedOffset + intSize;         // objects come first, or at least at the first 4-byte boundary (a few bools can sneak in first to get us up to the boundary)
            public static readonly int _derivedIntExpectedOffset = _derivedObjectExpectedOffset + pointerSize;    // comes after _derivedObject because the former is an object                  
            public static readonly int _derivedBoolCExpectedOffset = _derivedIntExpectedOffset + intSize;        // this one gets tacked on the end, because we have used up the 2-byte "gap" at the end of the 4-byte "section"

        }

    }

 


}
